package service

import (
	"database/sql"
	"duoke/context"
	"duoke/internal/dao"
	"duoke/internal/model/entity"
	"duoke/model"
	"duoke/tables"
	utils "duoke/utils/common"
	"duoke/utils/gconv"
	"duoke/utils/gjson"
	"duoke/utils/gstr"
	"duoke/utils/orm"
	"duoke/utils/wechat_v3"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/gogf/gf/v2/container/garray"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/util/grand"
	"math"
	"reflect"
)

var User = new(userService)

type userService struct{}

// 账户登录
func (s *userService) Login(c *gin.Context, openid string, sessionKey string, unionid string) (*tables.User, error) {
	accountR, _ := Request.Account(c)
	user := new(tables.User)
	has := false
	err := errors.New("异常错误")
	has, err = orm.Db.Where("acid  =? AND openid=?", accountR.Id, openid).Get(user)
	if err != nil {
		return nil, err
	}

	if has {
		if user.IpAddr == "" {
			// 用户登录获取ip
			user.IpAddr = context.Global.Ip
		}
		if user.SubMchId == "" {
			user.SubMchId = "1717531771"
		}
		user.SessionKey = sessionKey
		_, err := orm.Db.ID(user.Id).Where("acid =?", accountR.Id).Cols("session_key").Update(user)
		if err != nil {
			return nil, err
		}
	} else {
		// 用户登录获取ip
		getIp, err := utils.Common.GetIp()
		if err != nil {
			return nil, err
		}
		user.IpAddr = getIp.Query
		user.Acid = accountR.Id
		user.Openid = openid
		user.SessionKey = sessionKey
		user.Unionid = unionid
		user.Money = "0"
		user.UserMoney = "0"
		user.IpAddr = context.Global.Ip
		user.SubMchId = "1717531771"
		affected, err := orm.Db.Insert(user)
		if err != nil {
			return nil, err
		}
		if affected == 0 {
			return nil, errors.New("创建登录数据失败")
		}
	}
	return user, nil
}

// 获取用户信息
func (s *userService) GetById(c *gin.Context, id int, field string) (*tables.User, error) {
	accountR, _ := Request.Account(c)
	data := new(tables.User)
	has, err := orm.Db.ID(id).Where("acid =?", accountR.Id).Cols(field).Get(data)
	if err != nil {
		return nil, err
	}
	if !has {
		return nil, errors.New("该用户不存在")
	}
	return data, nil
}

// 账户信息
func (s *userService) Info(c *gin.Context, field string) (*tables.User, error) {
	userR, _ := Request.User(c)
	accountR, _ := Request.Account(c)
	user := new(tables.User)
	has, err := orm.Db.ID(userR.Id).Where("acid =?", accountR.Id).Cols(field).Get(user)
	if err != nil {
		return nil, err
	}
	if !has {
		return nil, errors.New("用户信息不存在")
	}
	return user, nil
}

// 账户保存
func (s *userService) UpdateInfo(c *gin.Context, req model.ReqUser) error {
	accountR, _ := Request.Account(c)
	userR, _ := Request.User(c)
	user := new(tables.User)
	user.Nickname = req.Nickname
	user.Avatar = req.Avatar
	user.Name = req.Name
	user.Phone = req.Phone
	user.SubMchId = req.SubMchId
	_, err := orm.Db.ID(userR.Id).Where("acid =?", accountR.Id).Update(user)
	return err
}

// 手机号保存
func (s *userService) UpdatePhone(c *gin.Context, phone string) error {
	userR, _ := Request.User(c)
	accountR, _ := Request.Account(c)
	user := new(tables.User)
	user.Phone = phone
	_, err := orm.Db.ID(userR.Id).Where("acid =?", accountR.Id).Cols("phone").Update(user)
	return err
}

// 列表
func (s *userService) Page(c *gin.Context, req model.ReqList) ([]tables.User, int64, error) {
	accountR, _ := Request.Account(c)
	// 分页
	offset := (req.PageNo - 1) * req.PageSize
	list := make([]tables.User, 0)
	query := orm.Db.Where("acid =?", accountR.Id).Desc("id")
	if req.Keyword != "" {
		query = query.Where("(id =? OR nickname LIKE ? OR name LIKE ? OR phone LIKE ?)", req.Keyword, "%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
	}
	total, err := query.Limit(req.PageSize, offset).FindAndCount(&list)
	if err != nil {
		return nil, 0, err
	}
	return list, total, nil
}
func (s *userService) Save(c *gin.Context, req model.ReqGoodsUser) error {
	accountR, _ := Request.Account(c)
	if req.Id > 0 {
		// 保存数据
		_, err := orm.Db.ID(req.Id).Where("acid =?", accountR.Id).Cols("nickname", "avatar", "name", "phone").Update(req)
		if err != nil {
			return err
		}
	} else {
		// 保存数据
		req.Acid = accountR.Id
		_, err := orm.Db.Insert(req)
		if err != nil {
			return err
		}
	}
	return nil
}

// 删除用户
func (s *userService) Del(c *gin.Context, req model.ReqId) error {
	accountR, _ := Request.Account(c)
	_, err := orm.Db.ID(req.Id).Where("acid =?", accountR.Id).Delete(new(tables.User))
	if err != nil {
		return err
	}
	return nil
}

// 获取用户值
func (s *userService) GetValueById(id int, field string) string {
	user := new(tables.User)
	has, err := orm.Db.ID(id).Cols(field).Get(user)
	if err != nil {
		return ""
	}
	if !has {
		return ""
	}
	return reflect.ValueOf(*user).FieldByName(gstr.CamelString(field)).String()
}

// 机器人列表
func (s *userService) RobotList(c *gin.Context, req model.ReqList) ([]tables.Robot, error) {
	accountR, _ := Request.Account(c)
	list := make([]tables.Robot, 0)
	query := orm.Db.Desc("id").Where("acid =?", accountR.Id)
	if req.Keyword != "" {
		query = query.Where("nickname =?", req.Keyword)
	}
	err := query.Find(&list)
	if err != nil {
		return nil, err
	}
	return list, nil
}

func (s *userService) RobotSave(c *gin.Context, req model.ReqRobot) error {
	accountR, _ := Request.Account(c)
	if req.Id > 0 {
		// 保存数据
		_, err := orm.Db.ID(req.Id).Where("acid =?", accountR.Id).Cols("nickname", "avatar").Update(req)
		if err != nil {
			return err
		}
	} else {
		// 保存数据
		req.Acid = accountR.Id
		_, err := orm.Db.Insert(req)
		if err != nil {
			return err
		}
	}
	return nil
}
func (s *userService) RobotDel(c *gin.Context, req model.ReqIds) error {
	accountR, _ := Request.Account(c)
	_, err := orm.Db.Where("acid =?", accountR.Id).In("id", req.Ids).Delete(new(tables.Robot))
	if err != nil {
		return err
	}
	return nil
}
func (s *userService) UserMoney(c *gin.Context, uid int, money float64, remark string, type2 string) error {
	var res sql.Result
	var err error
	if money > 0 {
		res, err = dao.User.Ctx(c).Where("id", uid).Unscoped().Increment("user_money", math.Abs(money))
	} else {
		res, err = dao.User.Ctx(c).Where("id", uid).Unscoped().Decrement("user_money", math.Abs(money))
	}
	if err != nil {
		return err
	}
	if rows, _ := res.RowsAffected(); rows == 0 {
		return errors.New("用户金额操作失败")
	}
	if type2 == "" {
		type2 = "user_money"
	}
	dao.UserMoneyLog.Ctx(c).Data(g.Map{
		"type":       type2,
		"user_id":    uid,
		"order_no":   gtime.Now().Format("YmdHis") + "ur" + gconv.String(grand.N(0, 99)),
		"amount":     money,
		"created_at": gtime.Now().Timestamp(),
		"remark":     remark,
		"status":     1,
	}).Unscoped().Insert()
	return nil
}

// Money 充值余额
func (s *userService) Money(c *gin.Context, uid int, money float64, remark string, type2 string) error {
	var res sql.Result
	var err error
	if money > 0 {
		res, err = dao.User.Ctx(c).Where("id", uid).Unscoped().Increment("money", math.Abs(money))
	} else {
		res, err = dao.User.Ctx(c).Where("id", uid).Unscoped().Decrement("money", math.Abs(money))
	}
	if err != nil {
		return err
	}
	if rows, _ := res.RowsAffected(); rows == 0 {
		return errors.New("用户金额操作失败")
	}
	if type2 == "" {
		type2 = "money"
	}
	dao.UserMoneyLog.Ctx(c).Data(g.Map{
		"type":       type2,
		"user_id":    uid,
		"order_no":   gtime.Now().Format("YmdHis") + "ur" + gconv.String(grand.N(0, 99)),
		"amount":     money,
		"created_at": gtime.Now().Timestamp(),
		"remark":     remark,
		"status":     1,
	}).Unscoped().Insert()
	return nil
}
func (s *userService) Score(c *gin.Context, uid int, score int, remark string) error {
	var res sql.Result
	var err error
	if score > 0 {
		res, err = dao.User.Ctx(c).Where("id", uid).Unscoped().Increment("score", math.Abs(gconv.Float64(score)))
	} else {
		res, err = dao.User.Ctx(c).Where("id", uid).Unscoped().Decrement("score", math.Abs(gconv.Float64(score)))
	}
	if err != nil {
		return err
	}
	if rows, _ := res.RowsAffected(); rows == 0 {
		return errors.New("用户积分操作失败")
	}
	dao.UserScoreLog.Ctx(c).Data(g.Map{
		"type":       "default",
		"user_id":    uid,
		"score":      score,
		"remark":     remark,
		"created_at": gtime.Now().Timestamp(),
	}).Unscoped().Insert()
	return nil
}
func (s *userService) ScoreV2(uid int, score int, remark string) error {
	var res int64
	var err error
	if score > 0 {
		res, err = orm.Db.ID(uid).
			Incr("score", math.Abs(gconv.Float64(score))).Update(&tables.User{})
	} else {
		res, err = orm.Db.ID(uid).
			Decr("score", math.Abs(gconv.Float64(score))).Update(&tables.User{})
	}
	if err != nil {
		return err
	}
	if res == 0 {
		return errors.New("积分添加失败")
	}
	_, err = orm.Db.Table("user_score_log").Insert(g.Map{
		"type":       "default",
		"user_id":    uid,
		"score":      score,
		"remark":     remark,
		"created_at": gtime.Now().Timestamp(),
	})
	if err != nil {
		return err
	}
	return nil
}
func (s *userService) SaveV2(c *gin.Context, req *model.ReqUser) error {
	accountR, _ := Request.User(c)
	if accountR.Id > 0 {
		// 保存数据
		req.Id = accountR.Id
		_, err := orm.Db.ID(req.Id).Where("acid =?", accountR.Acid).Omit("money").
			Update(req)
		if err != nil {
			return err
		}
	} else {
		// 保存数据
		return errors.New("用户不存在")
	}
	return nil
}
func (s *userService) WithdrawalList(c *gin.Context, req *model.ReqList) (*model.RspList, error) {
	var m []model.JoinWithdrawal
	count, err := tableCommon.QueryListWithPageV2("", req.PageNo, req.PageSize).
		Table("withdrawal").Alias("a").
		Join("left", "user as u", "a.user_id=u.id").
		Cols("a.id,user_id,nickname,avatar,a.money,feel,state,reason,a.deleted_at,freeze_amount").
		FindAndCount(&m)
	if err != nil {
		return nil, err
	}
	return &model.RspList{
		List:  &m,
		Total: count,
	}, nil
}
func (s *userService) WithdrawalSave(c *gin.Context, id, state int, reason string) error {
	// 擦汗寻提现列表是否存在数据
	var withdrawal tables.Withdrawal
	get, err := orm.Db.ID(id).Table("withdrawal").Get(&withdrawal)
	if err != nil || !get {
		return errors.New("信息不存在")
	}
	array := garray.NewFrom([]interface{}{1, 4, 5})
	// 判断withdrawal.state是否在 str里面
	if array.Contains(withdrawal.State) {
		return errors.New("已提现")
	}

	withdrawal.State = state
	withdrawal.Reason = reason
	//  如果state等于1 表示该接口要开始提现 调用微信（新版提现接口）https://pay.weixin.qq.com/doc/v3/merchant/4012716434
	if state == 1 {
		err = s.TransferBills(c, &withdrawal)
		if err != nil {
			return err
		}
		withdrawal.State = 4
		withdrawal.FreezeAmount = 0
	}
	// 如果等于3则退回佣金 审核驳回
	if state == 3 {
		if withdrawal.FreezeAmount <= 0 {
			return errors.New("该用户的金额已经提现")
		} else {
			User.UserMoney(c, withdrawal.UserId, gconv.Float64(withdrawal.Money),
				"驳回提现请求", "")
			withdrawal.FreezeAmount = 0
		}
	}
	update, err := orm.Db.ID(id).AllCols().Update(&withdrawal)
	if err != nil || update == 0 {
		return errors.New("修改失败")
	}
	return err
}

// TransferBills 开始提现
func (s *userService) TransferBills(c *gin.Context, req *tables.Withdrawal) error {
	user, err := s.GetById(c, req.UserId, "id,avatar,openid,acid")
	if err != nil {
		return errors.New("用户信息有误")
	}
	// 获取order的生成方式
	order := utils.Common.GetOrder()
	transfer := model.Transfer{
		OutBillNo:       order,
		UserId:          user.Id,
		Avatar:          user.Avatar,
		WithdrawalId:    req.Id,
		TransferSceneId: "1000",
		Openid:          user.Openid,
		TransferAmount:  gconv.Int(req.FreezeAmount * 100),
		TransferRemark:  "用户提现",
		NotifyUrl:       context.Global.Domain + "/mp/notify/withdrawal",
		TransferSceneReportInfos: []map[string]interface{}{
			{
				"info_type":    "活动名称",
				"info_content": "用户提现",
			},
			{
				"info_type":    "奖励说明",
				"info_content": "用户获取提现金额",
			},
		},
	}
	// 获取当前小程序的配置信息
	res, err := AccountSetting.InfoById(user.Acid, "miniprogram")
	if err != nil {
		return errors.New("获取小程序配置失败，请检查小程序配置")
	}
	rsp, err := wechat_v3.NewConfig(res).Transfer(c, &transfer)
	var t map[string]interface{}
	if err != nil {
		gjson.Json2StructOrMap(err.Error(), &t)
		return errors.New(gconv.String(t["message"]))
	}
	if rsp.Error != "" {
		return errors.New(rsp.Error)
	}
	// 转账完成后添加提现记录
	log := entity.WithdrawalLog{
		UserId:         user.Id,
		OutBillNo:      rsp.Response.OutBillNo,
		TransferBillNo: rsp.Response.TransferBillNo,
		State:          rsp.Response.State,
		Openid:         user.Openid,
		Avatar:         user.Avatar,
		TotalAmount:    gconv.Int(req.FreezeAmount * 100),
		WithdrawalId:   req.Id,
		PackageInfo:    rsp.Response.PackageInfo,
		SubMchId:       gconv.String(res["pay_mch_id"]),
	}
	err = Withdrawal.Save(c, &log)
	if err != nil {
		return err
	}
	return nil
}
